For this algorithm many things which were already said for Scroller_XUnlimited are also true. So read by all means first this algorithm's documentation!

The big difference is that the bitmap is no longer double-wide. For 1x and 2x FETCH Modes an EXTRAWIDTH of 32 (2 * BLOCKWIDTH) is enough. For 4x FETCH Mode we need to have a EXTRAWIDTH of 64 so that the bitmap width becomes a multiple of 64. So the bitmap width will either be 352 or 384 pixels. We also no longer do double-blits. Therefore this alogrithm is twice as fast.

As you probably have already seen in the overview table, the height of the bitmap depends on the width of the map. Very strange, isn't it? The reason is, that when scrolling right we simply increase the plane start addresses (when scrolling left we decrease them). For example if we scroll right 16 pixels, then the plane start addresses will have to be set to REAL-BITMAP-START-ADDRESS + 2 bytes (for chunky pixel mode = GFX cards it would be 16 bytes). Okay, so this can only mean that the bitmap needs to be 2 bytes bigger, when we scroll right 16 pixels maximum, 32 pixels of maximum right-scrolling would mean 4 bytes of extra size, etc. If one thinks of extra large autoscrolling screens one might think that much more extra-memory is required (16 pixels: 2 * 256 * NUMPLANES). That is not true, because in contrast to this screens we do not keep all the graphic data in the bitmap, just a small part of it (352 * 256 or 384 * 256 pixels) . When we scroll right, at the left side some area of the bitmap disappears and at the right side it reappears plane-shifted (on GFX cards or with bitmaps in non-interleaved format we would get line- shifting). Here we can blit the new map area coming in from the right side. If we use AllocBitmap() to create the bitmap, then the forumla to calculate the overall bitmap height looks like this:

  overallbitmapheight = BITMAPHEIGHT + (MAPWIDTH : BITMAPBLOCKSPERROW : NUMPLANES) + 1

BITMAPHEIGHT is the actually wanted bitmap height, in our case 256 pixels. MAPWIDTH is the width of the map in blocks. BITMAPBLOCKSPERROW equals BITMAPWIDTH : BLOCKWIDTH, that is 22 (1x and 2x FETCH Mode) or 24 (4x FETCH Mode) in our case.

This algorithm's demo program uses a map which is 1000 blocks wide. 1000 blocks are 16000 pixels or 50 screens. It's quite big and anyway the above formula gives a very small overall bitmap height as result - an extremely big saving of memory compared to the Scroller_XUnlimited algorithm, especially if you take into account that almost every game uses triple- or at least double-buffering:

  overallbitmapheight = 256 + (1000 : 22 : 4) + 1
  overallbitmapheight = 256 + 11 + 1
 
  overallbitmapheight = 268

268 pixels of overall bitmap height (for the 4x FETCH Mode it would be 267 pixels) is very little, if you look at the size of the map. And even a giant map being 200 screens (64000 pixels) wide only needs a overall bitmap height of 301 (4x: 297) pixels. Still much less memory than needed by the Scroller_XUnlimited algorithm. BTW: On a GFX card or with bitmaps in non-interleaved format more memory would be required. In contrast to the 2 bytes per 16 pixel scrolling with an interleaved Amiga planar bitmap, a gfx card (chunky pixel) would need 16 bytes per 16 pixel scrolling and a non-interleaved Amiga planar bitmap still 2 bytes * NUMPLANES per 16 pixel scrolling.

In the following descriptions I assume that we use 1x or 2x FETCH Mode, that is a bitmap width of 352 pixels. At startup we fill the bitmap with the initially visible map area:

The red rectangle shows the initial actual-bitmap-area, the yellow rectangle shows the initial visible-bitmap-area. Note the empty area at the bottom which will only be used (and be used more and more) when we scroll right. Let's assume we want to go 16 pixels to the right. The actual-bitmap-area and with it the visible area moves 16 pixels to the right, that is 2 bytes back in memory. It will look like this:

The end of the red rectangle, that is the actual-bitmap-area, does not go beyond the right end of the bitmap, because there is nothing (no memory). Instead it "wraps" to the left side. The magnified parts of the abouve picture make clear that the area there is shifted down by one "planeline" (1 pixelline consists auf NUMPLANES planelines). The following two pictures show why. The first picture shows two pixellines as they are in memory. The numbers in the boxes, each of the boxes stands for 16 pixels, show the offset from the bitmap address in bytes. For the first picture we assume, that we want to display the bitmap at offset 0:

In the second picture the videochip is supposed to display the bitmap at offset 2. As you can see in the picture, the Video DMA gets the data from offsets in ascending order. For example at the right border of the bitmap in the first line (= line 0) the plane 0 pointer is at offset 42. The next higher offset is offset 44 which is at the left border of the bitmap, but not in the same planeline - instead it is in the planeline below it.

The X position of the fillup-column in the bitmap is the same as in Scroller_XUnlimited algorithm. We take the position of the actual-bitmap-area (red rectangle) and round it down to a multiple of a block's width:

videoposx & ~(BLOCKWIDTH - 1)

For the Y position it's a little bit different. In Scroller_XUnlimited it was always at the very top of the bitmap. But here when scrolling right we increase the variable videoposx again and again without ever setting it back to 0 at a certain position. Therefore the fillup-column slowly goes down in the (real) bitmap. This happens automatically and is not a problem at all, because when blitting we take the variable videopox into account, that is, we add it to the destination X coordinate, which indirectly does or may affect the destination Y coordinate. If for example we have a coordinate pair (x=0,y=1) where y is in planelines (1 pixelline consists of NUMPLANES planelines) then another coordinate pair (x=352,y=0) is equivalent, if we assume a bitmap width of 352 pixels.

As described earlier, we must take care that when scrolling right, areas which disappear at the left side are displayed plane-shifted at the right side by the Video CHIP. From the bitmap's point of view the plane-shifted area is at the left side. That's exactly where we must blit, because it's the area which comes in at the right side. Since the area is plane-shifted, blitting also needs to happen plane-shifted, that is the real destination Y coordinate of the single blocks we have to blit, needs to be increased by 1 planeline. The easiest way to do this, is to add BITMAPWIDTH (352) to the destination X (X not Y!) coordinate. By doing so we don't need to take extra care of the y positions. We simply can assume, that the fillup-column always starts at y position 0.

After having scrolled for example 2 pixels to the right, the bitmap will look like this:

The yellow rectangles show the blitted = new coming in blocks. Compared to the other blocks they were blitted one planeline below. If now we want to go 1 pixel to the left again, then the second yellow block which actually is block (22,1) (that is the 23rd block of the 2nd map row) will have to be replaced with block (0,1) (that is 1st block of 2nd map row). As you can see on the other non-yellow blocks, we must not do plane-shifted blits when scrolling to the left. Therefore this time we will not add BITMAPWIDTH to the destination X coordinate. After the blit, we'll have a small problem, as you can see in the following picture:

Since when scrolling right we did plane-shifted blits (destination Y is one planeline below normal blocks), the second yellow block destroyed two "old" blocks. For the second destroyed block the damaged area is only one planeline (for a block width of 16 pixels one planeline is 2 bytes = 1 word), but anyway, it is and remains a graphic error. Therefore we also have to restore this area. We'll see later, how. Now we are on map position 1. If we want to go 1 pixel to the right again to go to map position 2, we'll have a similiar problem:

When scrolling left by one pixel we also partly over-blitted the first yellow block. Therefore when changing direction and scrolling right again it is not enough to just blit the 2nd yellow block. We also have to restore the destroyed parts of the 1st yellow block.

Very complicated and much work? No, not at all! When looking at the previous three pictures one can note that when scrolling right the first planeline = the first 2 bytes of the next (staying below) block get trashed. And when scrolling left the blit trashes the last planeline = the last 2 bytes of the previous (staying above) block.

So here's what we do to solve the problem: before doing plane-shifted block blits, that is when scrolling right, we make a backup of the last block planeline in the bitmap that will be over-blitted - that is the first planeline (= 1st line, 1st plane) of the next (non-planeshifted) block below it. The planeline is only 2 bytes big, so we save it in a normal 16 bit UWORD variable. In the source code it is called saveword. We also save the memory address from where we read the planeline, because we need it later for restoring. In the sourcecode the variable savewordpointer is used for this matter.

Before doing non-plane-shifted block blits, that is when scrolling left, we make a backup of the first block planeline in the bitmap that will be over-blitted - that is the last planeline (= last line, last plane) of the previous (planeshifted) block above it. Again we save the content of the planeline in a variable and also save the address of the planeline so that we don't need to recalculate everything when restoring.

As long as we scroll in the same direction, no restoring is necessary. Restoring is only necessary when we change direction. The backup must be done always. In pseudo code it looks like this:

 SCROLL_LEFT:

   /* Restoring */

   if (Previous_ScrollDirection == RIGHT)
   {
       copy saveword to savewordpointer;
   }
   
   /* Backup */

   savewordpointer = address of the first planeline that will be overblitted
   saveword = content of savewordpointer

   /* Blit */
   
   Blit block
   
   Previous_ScrollDirection = LEFT

   return



 SCROLL_RIGHT:

   /* Restoring */

   if (Previous_ScrollDirection == LEFT)
   {
       copy saveword to savewordpointer
   }
   
   /* Backup */

   savewordpointer = address of the last planeline that will be overblitted
   saveword = content of savewordpointer

   /* Blit */
   
   Blit block
   
   Previous_ScrollDirection = RIGHT

   return

If we want to use the 2x and 4x FETCH Modes we must take care to have the hidden area at the right place. Like in Scroller_XUnlimited algorithm we do this by adding an offset of 2 (2x FETCH Modus) or 6 (4x FETCH Modus) to the real bitmap address when rendering/blitting into the bitmap. Because of this offset we must not forget to ask for one more line (AllocBitmap()) or 2 or 6 more bytes (AllocMem()) when allocating the bitmap.

Some more comments about the demo program's source code. When the variable mapposx, which contains the map position X in pixels is 0, then the user sees the map area beginning at position BLOCKWIDTH (16), because the first BLOCKWIDTH (16) pixels are always hidden. So what the user is seeing is the area:

(mapposx + BLOCKWIDTH,0) - (mapposx + BLOCKWIDTH + SCREENWIDTH - 1,SCREENHEIGHT - 1)

This means that the first block column of the map file is never visible. If you want to blit something into the bitmap for example blitter objects (BOBs) you must do it in the actual visible bitmap area. This area is:

(videoposx + BLOCKWIDTH,0) - (videoposx + BLOCKWIDTH + SCREENWIDTH - 1,SCREENHEIGHT - 1)

You are allowed to round down the start X coordinate (videoposx + BLOCKWIDTH) and round up the end X coordinate (videoposx + BLOCKWIDTH + SCREENWIDTH - 1) to a multiple of BLOCKWIDTH. This leads to a blitable area that is SCREENWIDTH + BLOCKWIDTH pixels wide:

blitarea_strtx = (videoposx + BLOCKWIDTH) & ~(BLOCKWIDTH - 1)

blitarea_endx = blitarea_strtx + SCREENWIDTH + BLOCKWIDTH - 1

Blit addresses must be based on the variable frontbuffer, because this variable does not equal the real bitmap address in 2x and 4x FETCH Modes. In the source code the variables mapposx and videoposx contain always the same values. Therefore it will be necessary to use 32 instead of 16 bits for some of the variables and function parameters if you are using very big maps, otherwise there will be overflows. 1 pixel of scrolling also means an increment or decrement of 1 of the variable videoposx!